package com.chrray.position.service.position.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.api.R;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chrray.position.auth.Authentication;
import com.chrray.position.auth.AuthenticationContext;
import com.chrray.position.domain.*;
import com.chrray.position.domain.dto.*;
import com.chrray.position.domain.dto.search.*;
import com.chrray.position.domain.vo.BossInfoDTO;
import com.chrray.position.domain.vo.FdJobPostingsVO;
import com.chrray.position.enums.YesNoEnum;
import com.chrray.position.enums.app.*;
import com.chrray.position.exception.ServiceException;
import com.chrray.position.mapper.position.FdJobPostingsMapper;
import com.chrray.position.mapper.position.FdJobWorkerRelMapper;
import com.chrray.position.service.position.FdJobService;
import com.chrray.position.tasks.JobTasks;
import com.chrray.position.util.redis.RedisUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * @author liujianxin
 * @date 2024/08/28 22:15
 */
@Service
@AllArgsConstructor
@Slf4j
public class FdJobServiceImpl extends ServiceImpl<FdJobPostingsMapper, FdJobPostings> implements FdJobService {
    private final FdJobPostingsMapper mapper;
    private final RedisUtils redisUtils;

    @Resource
    private FdJobWorkerRelMapper fdJobWorkerRelMapper;

    private boolean saveOrUpdateJob(FdJobPostings entity, FdJobPostingsDTO job) {


        Authentication context = AuthenticationContext.context();
        log.info("[发布职位] 用户 {} 发布职位{}", context.getUserId(), job);
        Assert.isTrue(context.isRealNamed(), "请先进行实名认证");

        entity.setUserId(context.getUserId());

        // 工种名称,小优快工上是手动填写,这里保持一致
        entity.setJobName(job.getJobName());
        entity.setJobCategory(job.getJobCategory());
        entity.setCalcMethod(job.getCalcMethod());
        entity.setSalaryPrice(job.getSalaryPrice());
        entity.setSalaryUnit(job.getSalaryUnit());
        Assert.isTrue(job.workDateValid(),"工作日期不合法");
        // 新农人
        if(job.getNewFarmer() != null && job.getNewFarmer()){
            entity.setJobType(FdJobType.NEW_FARMER.getValue());
            entity.setIsNewPeasant(YesNoEnum.YES.getCode());
            Assert.isTrue(StringUtils.hasLength(job.getTownship()),"乡镇不能为空");
            entity.setTownship(job.getTownship());
        }
        // 长期工
        else if(StringUtils.hasLength(job.getLongTermWokeDate())) {
            entity.setJobType(FdJobType.LONG_TERM.getValue());
            entity.setIsNewPeasant(YesNoEnum.NO.getCode());
        } else {
            entity.setJobType(FdJobType.SHORT_TERM.getValue());
            entity.setIsNewPeasant(YesNoEnum.NO.getCode());
        }
//        // 长期 = 计时&&单位为月
//        else if(job.getCalcMethod() == FdSalaryCalcMethodEnum.CALC_TIME
//                && job.getSalaryUnit() == FdSalaryUnitEnum.MONTH){
//            entity.setJobType(FdJobType.LONG_TERM.getValue());
//            entity.setIsNewPeasant(YesNoEnum.NO.getCode());
//
//        }
//        // 短期 = 计时&&单位不为月 或者 计件
//        else {
//            entity.setJobType(FdJobType.SHORT_TERM.getValue());
//            entity.setIsNewPeasant(YesNoEnum.NO.getCode());
//
//        }

        // 工作日期可以手动输入,也可以选择日期,我们使用3个字段保存
        entity.setWorkDateStart(job.getWorkDateStart());
        entity.setWorkDateEnd(job.getWorkDateEnd());
        entity.setLongTermWokeDate(ObjectUtils.isEmpty(job.getLongTermWokeDate())?null:job.getLongTermWokeDate());

        entity.setWorkTimeStart(job.getWorkTimeStart());
        entity.setWorkTimeEnd(job.getWorkTimeEnd());
        entity.setWorkAddress(job.getWorkAddress());
        entity.setWorkAddressDetail(job.getWorkAddressDetail());
        entity.setLat(job.getLat());
        entity.setLon(job.getLon());
        String name = mapper.findDistrictName(Long.valueOf(job.getDistrictId()));
        entity.setDistrictId(job.getDistrictId());
        entity.setDistrictName(name);
        entity.setWorkContent(job.getWorkContent());
        if(!CollectionUtils.isEmpty(job.getRelatedImges())) {
            entity.setRelatedImges(String.join(",",job.getRelatedImges()));
        }
        entity.setNumberOfWorkers(job.getNumberOfWorkers());
        entity.setGender(job.getGender());
        entity.setAgeStart(job.getAgeStart());
        entity.setAgeEnd(job.getAgeEnd());
        entity.setHeightStart(job.getHeightStart());
        entity.setHeightEnd(job.getHeightEnd());
        entity.setRequiresExperience(job.getRequiresExperience());
        entity.setContactName(job.getContactName());
        entity.setContactPhone(job.getContactPhone());
        entity.setStatus(FdJobStateEnum.PENDING.getValue());
        entity.setAdminPublished(YesNoEnum.NO.getCode());
        entity.setPublishTime(new Date());
        entity.setAreaId(job.getAreaId());
        // 职位发布人
        if(context.isEntRealNamed()) {
            FdAuthEnterprise enterprise = new FdAuthEnterprise().findOneByUserId(context.getUserId());
            entity.setPublishBy(enterprise.getEnterpriseName());
            entity.setPublishType(FdJobPubTypeEnum.ENTERPRISE.getValue());
        } else {
            FdAuthPersonal personal = new FdAuthPersonal().findOneByUserId(context.getUserId());
            entity.setPublishBy(personal.getName());
            entity.setPublishType(FdJobPubTypeEnum.PERSONAL.getValue());
        }
        entity.setDelFlag(YesNoEnum.NO.getCode());
        if(entity.getId() == null) {
            entity.setStaTime(new Date());
            return entity.insert();
        } else {
            return entity.updateById();
        }
    }

    /**
     * 雇主发布职位
     *  1.发布后需要后台审核
     *
     * 岗位类型:
     * 1- 新农人,手动选择
     * 2- 长期工,薪酬为计时,且单位为月
     * 3- 短期工,薪酬为计时&&单位不为月 或者 计件
     *
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void publishJob(FdJobPostingsDTO job) {
        saveOrUpdateJob(new FdJobPostings(),job);
    }

    /**
     * 首页-附近的职位
     */
    @Override
    public IPage<GrabJobsDTO> nearbyJobs(GrabJobSearch search) {
        Authentication context = AuthenticationContext.context();
        // update user current location when who are fetching jobs
        //mapper.updateUserCurrentLocation(context.getUserId(),search);
        IPage<GrabJobsDTO>  page = new Page<>(search.getPageNum(), search.getPageSize());
        List<GrabJobsDTO> list =  mapper.nearbyJobs(page,search,context.getUserId());
        page.setRecords(list);
        return page;
    }

    /**
     * 职位查询列表
     *  已经抢单的还能不能查出来?
     *      不能
     *  职位状态过滤: 0-待审核／-1 驳回／5-匹配中/1-进行中／2-已完成／3-已结束/4-已下架
     *      只能查询出 5-匹配中 的职位
     */
    @Override
    public IPage<GrabJobsDTO> pageList(JobPageSearch search) {
        Authentication context = AuthenticationContext.context();
        IPage<GrabJobsDTO>  page = new Page<>(search.getPageNum(), search.getPageSize());
        List<GrabJobsDTO> list = mapper.pageList(page,search,context.getUserId());
        page.setRecords(list);
        return page;
    }

    /**
     * 抢单
     */
    @Override
    public boolean grabJob(Long jobId) {
        Authentication context = AuthenticationContext.context();
        Assert.isTrue(context.isPersonalRealNamed(),"请先进行个人实名认证!");
        int effectCnt = mapper.grabJob(jobId,context.getUserId());
        Assert.isTrue(effectCnt > 0,"请勿重复抢单");
        return true;
    }

    /**
     * 职位详情
     * 1. 职位可以分享,分享出去的职位详情页,不需要登录;
     *    申请一个token,用于分享出去的职位详情页,不需要登录
     */
    private final static SimpleDateFormat sdf = new SimpleDateFormat("MM月dd日");
    private final static SimpleDateFormat sdfYYY = new SimpleDateFormat("yyyy-MM-dd");
    @Override
    public FdJobPostingsVO jobDetail(Long jobId) {
        FdJobPostings entity = mapper.selectById(jobId);
        Assert.notNull(entity,"职位不存在");
        FdJobPostingsVO vo = new FdJobPostingsVO();
        BeanUtils.copyProperties(entity,vo);
        vo.setJobId(entity.getId());
        // 工作日期
        if(StringUtils.hasLength(entity.getLongTermWokeDate())) {
            vo.setWorkDate(entity.getLongTermWokeDate());
        } else if(entity.getWorkDateStart() != null && entity.getWorkDateEnd() != null) {
            Date workDateStart = entity.getWorkDateStart();
            Date workDateEnd = entity.getWorkDateEnd();
            String start = sdf.format(workDateStart);
            String end = sdf.format(workDateEnd);

            vo.setWorkDate(start+ "-" + end);

            vo.setWorkDateStart(sdfYYY.format(workDateStart));
            vo.setWorkDateEnd(sdfYYY.format(workDateEnd));
        }
        // 检查是否已经收藏
        Long userId = AuthenticationContext.context().getUserId();
        boolean haCollected = new FdUserFavorites().haCollected(userId, jobId);
        vo.setHasCollected(haCollected);

        if(entity.getJobCategory() != null) {
            String jobCategoryName = FdJobSubcategory.findNameById(entity.getJobCategory());
            vo.setJobCategoryName(jobCategoryName);
        }
        LambdaQueryWrapper<FdJobWorkerRel> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(FdJobWorkerRel::getJobId,jobId);
        lambdaQueryWrapper.eq(FdJobWorkerRel::getUserId,userId);
        lambdaQueryWrapper.last(" limit 1");
        FdJobWorkerRel fdJobWorkerRel = fdJobWorkerRelMapper.selectOne(lambdaQueryWrapper);
        if(ObjectUtils.isEmpty(fdJobWorkerRel)){
            vo.setJobWorkerState(0);
        }else {
            vo.setJobWorkerState(fdJobWorkerRel.getState());
        }
        // 查询雇主信息
        // 已发布职位,招募中的
        BossInfoDTO bossInfo = mapper.findBossInfo(jobId);
        vo.setBossInfo(bossInfo);
        return vo;
    }

    /**
     * 推荐职位
     *  零工推零工
     *  长期推长期
     *  新农人推新农人
     *  按距离排序
     *
     */
    @Override
    public List<GrabJobsDTO> recommend(JobPageSearch search) {
        // 直接用附近的职位
        GrabJobSearch grabJobSearch = new GrabJobSearch();
        grabJobSearch.setLat(search.getLat());
        grabJobSearch.setLon(search.getLon());
        grabJobSearch.setJobType(search.getJobType());
        grabJobSearch.setDistance(10000);
        return nearbyJobs(grabJobSearch).getRecords();
    }

    /**
     * 我的报名/我的零工
     */
    @Override
    public IPage<GrabJobsDTO> myApply(MyJobSearchSearch search) {
        Authentication context = AuthenticationContext.context();
        Long userId = context.getUserId();
        IPage<GrabJobsDTO>  page = new Page<>(search.getPageNum(), search.getPageSize());
        List<GrabJobsDTO> list = mapper.myApply(page,search,userId);
        page.setRecords(list);
        return page;
    }

    /**
     * 雇工,对已经申请的职位取消申请
     * 前提是申请还没有被雇主录用
     */
    @Override
    public Boolean cancelApply(Long jobId) {
        Authentication context = AuthenticationContext.context();
        int effectCnt = mapper.cancelApply(jobId,context.getUserId());
        return effectCnt > 0;
    }

    /**
     * 用户收藏某个职位
     * @param jobId 职位id
     */
    @Override
    public Boolean collectJob(Long jobId) {
        Authentication context = AuthenticationContext.context();
        int effectCnt =mapper.collectJob(jobId,context.getUserId());
        return  effectCnt > 0;
    }

    /**
     * 我的收藏列表
     */
    @Override
    public IPage<GrabJobsDTO> favoritesList(MyJobSearchSearch search) {
        Authentication context = AuthenticationContext.context();
        Long userId = context.getUserId();
        IPage<GrabJobsDTO>  page = new Page<>(search.getPageNum(), search.getPageSize());
        List<GrabJobsDTO> list = mapper.favoritesList(page,search,userId);
        page.setRecords(list);
        return page;
    }

    /**
     * 雇主-我的发布
     */
    @Override
    public IPage<MyPubJobsDTO> bossMyPublish(BossJobPubSearch search) {
        Authentication context = AuthenticationContext.context();
        Long userId = context.getUserId();
        IPage<MyPubJobsDTO>  page = new Page<>(search.getPageNum(), search.getPageSize());
        List<MyPubJobsDTO> list = mapper.bossMyPublish(page,search,userId);
        page.setRecords(list);
        return page;
    }


    /**
     * 雇主-发布的职位详情
     */
    @Override
    public IPage<JobRelWorkerDTO> jobRelWorker(BossJobRelWorkerSearch search) {
        Authentication context = AuthenticationContext.context();
        Long userId = context.getUserId();
        // check if the job belongs to the user??

        IPage<JobRelWorkerDTO>  page = new Page<>(search.getPageNum(), search.getPageSize());
        List<JobRelWorkerDTO> list = mapper.jobRelWorker(page,search);
        page.setRecords(list);
        return page;
    }


    /**
     * 雇主-录用雇工
     * 1. 人数满了
     * 2. 到期了 1个月 {@link JobTasks#checkUpdateJobState()}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean employ(Long jobId, Long userId) {
        int effectCnt = mapper.employ(jobId,userId);
        boolean  ok = effectCnt > 0;
        if(ok) {
            // 检查录用人数
            mapper.tryUpdateJobState2Inprogress(jobId);
        }
        return ok;
    }

    /**
     * 雇主-对雇工完工
     */
    @Override
    public Boolean complete(Long jobId, Long userId) {
        int effectCnt = mapper.complete(jobId,userId);
        return effectCnt > 0;
    }

    /**
     * 将职位状态置为完工
     *  1.只能对进行中的职位进行完工
     *  2.只能操作自己的职位
     *  3. 已录用的雇工关系-->已完工
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean completeJob(Long jobId) {
        Authentication context = AuthenticationContext.context();
        Long bossId = context.getUserId();
        int effectCnt = mapper.completeJob(jobId,bossId);
        if(effectCnt > 0) {
            mapper.comlateJobWorkerRel(jobId);
        }
        return effectCnt > 0;
    }

    /**
     * 附近的零工,只看零工
     * 别把自己查出来了
     */
    @Override
    public IPage<NearbyWorkerDTO> nearbyWorkers(GrabJobSearch search) {
        Authentication context = AuthenticationContext.context();
        IPage<NearbyWorkerDTO>  page = new Page<>(search.getPageNum(), search.getPageSize());
        List<NearbyWorkerDTO> list = mapper.nearbyWorkers(page,search,context.getUserId());
        page.setRecords(list);
        return page;
    }

    /**
     * 对审核驳回的修改之后重新发布
     * 1. 状态位匹配中时,可以分享和结束
     * 2. 状态位进行中时,可以分享和完工
     *
     * 所有的重发都是新建一个职位,原来的职位不做任何修改
     */
    @Override
    public Boolean rePublish(FdJobPostingsDTO job) {
        FdJobPostings entity = mapper.selectById(job.getJobId());
        // 3.状态位已完成时,可以分享和重发
        if(entity.getStatus() == FdJobStateEnum.COMPLETED.getValue()) {
            return this.saveOrUpdateJob(new FdJobPostings(),job);
        }
        // 4.状态为待审核时,可以分享和重发
        if(entity.getStatus() == FdJobStateEnum.PENDING.getValue()) {
            return this.saveOrUpdateJob(new FdJobPostings(),job);
        }
        // 5.状态为审核驳回时,可以重发
        if(entity.getStatus() == FdJobStateEnum.REJECTED.getValue()) {
            return this.saveOrUpdateJob(new FdJobPostings(),job);
        }
        throw new ServiceException("当前状态不支持重新发布");
    }

    /**
     * 分享职位
     *  申请临时token 过期时间为7天?
     *  用于分享出去的职位详情页,不需要登录
     */
    @Override
    public String shareJob(Long jobId) {
        String token = UUID.randomUUID().toString().toString();
        String shareKey = "WX:SHARE:"+token;
        redisUtils.set(shareKey,jobId,60*60*24*7);
        return token;
    }

    @Override
    public Boolean cancelCollectJob(Long jobId) {
        Authentication context = AuthenticationContext.context();
        int effectCnt = mapper.cancelCollectJob(jobId,context.getUserId());
        return effectCnt > 0;
    }

    @Override
    public R endJob(Long jobId) {
        FdJobPostings fdJobPostings = mapper.selectById(jobId);
        if(ObjectUtils.isEmpty(fdJobPostings)){
            return R.failed("职位不存在");
        }
        if(!fdJobPostings.getStatus().equals(FdJobStateEnum.MATCHING.getValue())){
            return R.failed("状态不为匹配中");
        }
        LambdaQueryWrapper<FdJobWorkerRel> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(FdJobWorkerRel::getJobId,jobId);
        lambdaQueryWrapper.eq(FdJobWorkerRel::getState,FdWorkJobStateEnum.EMPLOYED.getValue());
        Integer integer = fdJobWorkerRelMapper.selectCount(lambdaQueryWrapper);
        if(integer>0){
            fdJobPostings.setStatus(FdJobStateEnum.IN_PROGRESS.getValue());
        }else {
            fdJobPostings.setStatus(FdJobStateEnum.COMPLETED.getValue());
        }
        mapper.updateById(fdJobPostings);
        return R.ok(fdJobPostings);
    }

    @Override
    public R<String> cancelJob(Long jobId, Long userId) {
        LambdaQueryWrapper<FdJobWorkerRel> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(FdJobWorkerRel::getJobId,jobId);
        lambdaQueryWrapper.eq(FdJobWorkerRel::getUserId,userId);
        lambdaQueryWrapper.last("limit 1");
        FdJobWorkerRel fdJobWorkerRel = fdJobWorkerRelMapper.selectOne(lambdaQueryWrapper);
        if(ObjectUtils.isEmpty(fdJobWorkerRel)){
            return R.failed("雇工不存在");
        }
        if(!fdJobWorkerRel.getState().equals(FdWorkJobStateEnum.EMPLOYED.getValue())){
            return R.failed("雇工状态不为已录用");
        }
        fdJobWorkerRel.setState(FdWorkJobStateEnum.CANCEL.getValue());
        fdJobWorkerRelMapper.updateById(fdJobWorkerRel);
        return R.ok("取消成功");
    }




}
